package com.company.exp2.util.Recorder;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class NoneDelayTimeRecorder implements Recordable {

    // count表示容器中的数据包个数
    int count;

    int maxCount;

    int No;

    // 记录存储键值对
    Map<Integer, Long> lengthRecordMap;

    // 等待时间存储键值对
    Map<Long, Integer> waitingRecordMap;

    // 记录进入时间点的键值对
    Map<Integer, Long> comingTimestampRecordMap;

    // 进入的数据包的编号
    int comingNumber;

    // 离开队列的数据包编号
    int leavingNumber;

    // 总时长
    long totalTime;

    // 队列长度结果集
    Map<Integer, Double> lengthResultMap;

    // 等待时间结果集
    Map<Long, Double> timeResultMap;

    {
        count = 0;
        lengthRecordMap = new HashMap<>();
        totalTime = 0L;
        lengthResultMap = new HashMap<>();
        waitingRecordMap = new HashMap<>();
        comingTimestampRecordMap = new HashMap<>();
        comingNumber = 1;
        leavingNumber = 1;
        timeResultMap = new HashMap<>();
    }

    @Override
    public void onPut(long timestamp) {
        recordLastStatus(timestamp);
        count++;
        recordTimestampForComingData(timestamp);
        comingNumber++;
        maxCount = Math.max(count, maxCount);
    }

    @Override
    public void onPop(long timestamp) {
        recordLastStatus(timestamp);
        count --;
        recordLeavingDataWaitingTime(timestamp);
        leavingNumber ++;
        ASSERT_COUNT_IS_POSITIVE();
    }

    @Override
    public void log() {
        logOutQueueDistributionResult();
        System.out.println("队列最大人数 : " + maxCount);
        logOutWaitingTimeDistributionResult();
    }

    @Override
    public Recordable setNo(int no) {
        No = no;
        return this;
    }

    /**
     *  私有区
     */

    private void logOutWaitingTimeDistributionResult() {
        System.out.println("no = " + No);
        File waitingTimeResultFile = new File("D:/A_3a/Computer_network/CSMACA/Simulation/src/com/company/exp2/resultLog/" + No +"/time_output.txt");
        EnsureFileExisted(waitingTimeResultFile);
        generateWaitingTimeResult();
        printTimeResult(waitingTimeResultFile);
    }

    private void logOutQueueDistributionResult() {
        File queueLengthResultFile = new File("D:/A_3a/Computer_network/CSMACA/Simulation/src/com/company/exp2/resultLog/" + No +"/queue_output.txt");
        EnsureFileExisted(queueLengthResultFile);
        generateQueueLengthResult();
        printLengthResult(queueLengthResultFile);
    }

    private void recordTimestampForComingData(long time) {
        ASSERT_NUMBER_UNIQUE_IN_MAP();
        comingTimestampRecordMap.put(comingNumber, time);
    }

    private void ASSERT_NUMBER_UNIQUE_IN_MAP() {
        assert !comingTimestampRecordMap.containsKey(comingNumber) : "duplication number in map!";
    }

    private void recordLeavingDataWaitingTime(long time) {
        ASSERT_NUMBER_EXISTS_IN_MAP();
        long waitingTime = time - comingTimestampRecordMap.get(leavingNumber);
        waitingTime /= 10000L;
        waitingTime *= 10000L;
        if (waitingRecordMap.containsKey(waitingTime)) {
            waitingRecordMap.put(waitingTime, waitingRecordMap.get(waitingTime) + 1);
        } else {
            waitingRecordMap.put(waitingTime, 1);
        }
        comingTimestampRecordMap.remove(leavingNumber);
    }

    private void ASSERT_NUMBER_EXISTS_IN_MAP() {
        assert comingTimestampRecordMap.containsKey(leavingNumber) : "How can leaving data number not exists in map ?";
    }

    private void recordLastStatus(long timestamp) {
        long interval = timestamp - totalTime;
        totalTime = timestamp;
        if (lengthRecordMap.containsKey(count)) {
            lengthRecordMap.put(count, lengthRecordMap.get(count) + interval);
        } else {
            lengthRecordMap.put(count, interval);
        }
    }

    private void printTimeResult(File file) {
        Set<Long> longs = timeResultMap.keySet();
        StringBuilder result = new StringBuilder("{");
        for (long i : longs) {
            result.append(" \"").append(i).append("\"").append(" : ").append(timeResultMap.get(i)).append(",");
        }
        result.replace(result.length() - 1, result.length() + 1, "}");
        printResultIntoFile(file, result.toString());
    }

    private void EnsureFileExisted(File file) {
        try {
            boolean isExisted = file.createNewFile();
            if (isExisted) {
                System.out.println("文件已存在, 已清空并重新创建");
                if (file.delete() && file.createNewFile()) {
                    System.out.println("创建成功");
                }
            } else {
                System.out.println("文件已被创建");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void printResultIntoFile(File file, String data) {
        try {
            FileWriter writer = new FileWriter(file);
            writer.write(data);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 生成分布函数图
    public void generateQueueLengthResult() {
        long accumulatedTime = totalTime;
        Set<Integer> integers = lengthRecordMap.keySet();
        for (int i : integers) {
            accumulatedTime -= lengthRecordMap.get(i);
            lengthResultMap.put(i, (accumulatedTime + 0.0) / totalTime);
        }
    }

    public void generateWaitingTimeResult() {
        long totalData = comingNumber - 1L;
        Set<Long> longs = waitingRecordMap.keySet();
//        Set<Long> sortedLongs = longs.stream().sorted().collect(Collectors.toCollection(LinkedHashSet::new));
        for (long i : longs) {
            totalData -= waitingRecordMap.get(i);
            timeResultMap.put(i, (totalData + 0.0) / (comingNumber - 1L));
        }
    }

    private void printLengthResult(File file) {
        Set<Integer> integers = lengthResultMap.keySet();
        StringBuilder result = new StringBuilder("{");
        for (int i : integers) {
            result.append(" \"").append(i).append("\"").append(" : ").append(lengthResultMap.get(i)).append(",");
            if (result.length() > 5000000) {
                printResultIntoFile(file, result.toString());
                result.delete(0, result.length());
            }
        }
        result.replace(result.length() - 1, result.length() + 1, "}");
        printResultIntoFile(file, result.toString());
    }

    private void ASSERT_COUNT_IS_POSITIVE() {
        assert count >= 0;
    }
}
